home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-20
/
rs0422.zip
/
LEVEL1
/
HDLSIO.AS
< prev
next >
Wrap
Text File
|
1990-10-28
|
18KB
|
642 lines
*TITLE HDLSIO - Z80 SIO HDLC DRIVER ROUTINES
; Driver routines to operate a port of a Zilog Z80-SIO in the SDLC/HDLC mode
; for packet radio operations. These routines operate in the half-duplex
; mode. The SIO RTS line is used to turn the transmitter on. Transmission
; of data will not begin until the SIO CTS line is asserted.
;
; Interface to the main program is via subroutine calls.
; Each call must be made with the address of the SIO data structure
; in the IY register. This data structure is as follows:
*INCLUDE STRUCT.LIB
*INCLUDE RCONFIG.LIB
; Upon activation, the main program should call the HDLINT initialization
; entry point to set up the SIO hardware. It is only necessary to call this
; routine once per SIO channel used. This routine is called only after a
; hardware reset as the routines will take care of all further SIO setup
; and disabling for the proper mode (Tx or Rx). When frame data is received,
; the external subroutine NEXTHI will be called to accept each data byte
; or to flag the end of frame. When transmission is desired, the TXSKED entry
; point must be called to schedule the transmission. As each data byte is
; required by the SIO transmitter, the external subroutine NEXTHO will
; be called. When this subroutine indicates the end of the data for the
; current transmission, the transmission will be terminated and the SIO placed
; back in receive mode. The external subroutine HODONE will then be called.
;
; All routines must be entered with the data structure address in IY.
; IX and IY registers are not affected by any of these routines.
; All external subroutines should return with IY unchanged.
;
; ENTRY POINTS:
;
; HDLI - Initialization subroutine. The SIO registers
; will be initialized in the receive mode.
; Entry: No parameters.
; Return: All registers undefined.
;
; XSKED - Schedule transmission.
; Entry: No parameters.
; Return: All registers undefined.
;
; CDS - Carrier detect status reporting.
; Entry: No parameters required
; Return: A = 0 if DCD off
; A = 0FFH if DCD on
;
; GETSIO - Convert HL from Channel # to SDS address
; Entry: HL = Channel number
; Return: HL = A(SDS) Block
; Z Flag set if out of bounds (and then HL=0)
; EXTERNAL ROUTINES
;
; All of these routines are called within interrupt service.
; External routines may destroy any registers except IY and those specified
; below with return parameters.
;
; NXTHO - Routine to supply next data byte for trans-
; mission.
; Entry: No parameters
; Return: A = 0FFH if next data byte
; = 0 if end of message (more to follow)
; = 1 if end of transmission (no CW id)
; = 2 if end of transmission and id request
; B = next data byte (if A = 0FFH)
;
; ODONE - Routine at end of transmission. Called after
; receive mode is re-entered. No parameters.
;
; NXTHI - Routine to accept received data. All data
; after a call with A = 0 is invalid if another
; call with A = 0 occurs before a call with
; A = 1.
; Entry: A = 0FFH if next data byte
; A = 0 if start of frame
; A = 1 if end of frame
; A = 2 if frame aborted due to an error
; B = next data byte (if A = 0 or 0FFH)
; Return: A = 0 if all ok
; A = FF if the frame should be aborted
*HEADING 'SIO Constants'
;COMMANDS:
RESTXI EQU 28H ;Reset TX interrupt
RESEXT EQU 10H ;Reset external interrupt
RESTCR EQU 80H ;Reset TX CRC generator
RESEOM EQU 0C0H ;Reset TX end of message
RESERR EQU 30H ;Reset error status
;STATUS BITS AND MASKS, RR 0
ABORT EQU 7 ;Break/abort
MABORT EQU 80H
EOM EQU 6 ;TX underrun/EOM
MEOM EQU 40H
CTS EQU 5 ;Clear-to-send line
MCTS EQU 20H
SYNC EQU 4 ;SDLC Sync/Hunt line
MSYNC EQU 10H
DCD EQU 3 ;Data-carrier-detect line
MDCD EQU 08H
TXBE EQU 2 ;TX buffer empty
MTXBE EQU 04H
RXBF EQU 0 ;RX buffer full
MRXBF EQU 01H
;STATUS BITS AND MASKS, RR 1
EOF EQU 7 ;End of SDLC frame
MEOF EQU 80H
FERR EQU 6 ;Framing error
MFERR EQU 40H
OVR EQU 5 ;RX overrun
MOVR EQU 20H
PERR EQU 4 ;Parity error
MPERR EQU 10H
MRES EQU 0EH ;Residue mask
ALL EQU 0 ;All sent
MALL EQU 1
RES8 EQU 0110B ;Byte boundry residue
MRTS EQU 02H ;Mask for RTS (WR 5)
MDTR EQU 80H ;Mask for DTR (WR 5)
GLOBAL HDLI,XSKED
; GLOBAL CDS
GLOBAL I_TBE,I_RBF,I_EXT,I_SRX
GLOBAL SETTMR,TICK
PSECT text
*HEADING 'Callable Subroutines'
; HDLI - SIO initialization routine
HDLI:
LD L,(IY+SINIT) ;SIO init data pointer, Lo byte
LD H,(IY+(SINIT+1));SIO init data pointer, Hi byte
LD B,(IY+SINTL) ;Length of init data
LD C,(IY+CPORT) ;SIO control port
;
HDLI1:
OTIR ;Init the SIO (rx mode)
XOR A
LD (IY+TSTA),A ;Tx mode off (state 0)
LD (IY+RSTA),A ;Rx state 0 (hunt)
RET
; XSKED - Schedule transmission
XSKED: CALL STDWAIT ;Start Dwait timer
RET NZ ;If DWAIT<>0 OR DWAIT==0 && DCD Return
DWDONE: CALL RTSON ;Start modem up
CALL STTXD ;Yes, Start TXDELAY timer
JP Z,STFRM ;Try to start the frame if TXD=0
RET
STDWAIT:
LD (IY+TSTA),5 ;Tx state = 5
BIT FULL,(IY+FLAG) ;Full Duplex port?
JP Z,1f ;Nope
XOR A ;Full Duplex port doesn't use DWait
RET
1: LD C,(IY+DWAT) ;Get DWAIT value
LD B,(IY+(DWAT+1))
LD A,B ;Check for no delay
OR C ;Well?
JP NZ,2f ;Start Timer
; ;No timer, check for carrier
LD C,(IY+CPORT) ;SIO control register
IN A,(C) ;Get status
XOR (IY+DCDTGL) ;Adjust Sense of DCD Pin
BIT DCD,A ;Do we have a nasty carrier?
RET ;If CCZ Then we can start sending (No DCD)
2: LD A,R ;Get a random number *EXPERIMENTAL*
AND 31 ;Make it 0-31 ms. (in tnc's it's 10ms steps)
ADD A,C ;Add it to the dwait
JP NC,1f
INC B ;Handle the carry
1: LD C,A
PUSH IY ;Get structure base addr
POP DE ;Timer service argument
LD HL,DSERVE ;Where timer should call
LD A,(IY+CHAN) ;Get channel #
CALL SETTMR ;Set the timer
LD (IY+TIMRUN),1 ;Show timer running
OR 1 ;Insure non-zero CC
RET
STTXD:
LD (IY+TSTA),1 ;Tx state = 1
LD C,(IY+TXDLY) ;Get TXDLY value
LD B,(IY+(TXDLY+1))
LD A,B ;Check for no delay
OR C ;Well?
RET Z ;No delay, skip timer
PUSH IY ;Get structure base addr
POP DE ;Timer service argument
LD HL,TSERVE ;Where timer should call
LD A,(IY+CHAN) ;Get channel #
CALL SETTMR ;Set the timer
LD (IY+TIMRUN),2 ;Show timer running
OR 1 ;Insure non-zero return
RET
; CDS - DCD line status reporting subroutine
;CDS:
; LD C,(IY+CPORT) ;SIO control port
; IN A,(C) ;Get ext status
; BIT DCD,A ;Test DCD line
; LD A,0 ;Assume off
; RET Z
; LD A,0FFH ;Flag it on
; RET
; GETSIO - Get pointer to SDS struct given a Channel number
GLOBAL SDS0,GETSIO
GETSIO:
PUSH DE
LD C,A ;SAVE A, WE ONLY TRASH BC (AND USE HL)
LD A,0 ;Insure H is 0
CP H
JP NZ,GETSIO0 ;Nope!
LD A,L ;Get Channel to search for
LD HL,SDS0 ;First SDS channel is SDS0
LD B,NUMCH
LD DE,SDSLEN
GNSIO:
CP (HL) ;CHAN is the first byte in the struc
JP Z,GETSIO1 ;Found it!
ADD HL,DE ;Try next
DJNZ GNSIO
GETSIO0:
LD HL,0 ;Not found.
GETSIO1:
LD A,L
OR H ;SET CC
LD A,C ;RESTORE A
POP DE
RET
*HEADING 'Tx Buffer Empty Interrupt Service'
; Transmitter buffer empty interrupt service.
; Action is as follows:
; State 0 (idle transmitter)
; Spurious interrupt -- ignore it.
; State 1 (waiting for CTS/timer)
; Spurious interrupt -- ignore it.
; State 2 (transmitting data)
; Call NEXTHO for data byte. If NEXTHO
; returns data, send to SIO. Otherwise,
; stuff dummy data to SIO. If end of
; transmission, state = 3, else 4.
; State 3 (end of frame)
; Call STFRM to re-start transmission.
; State = 2
; State 4 (end of transmission)
; Set RTS off, call HODONE to flag
; end of transmission, state = 0.
; State 5 (waiting for DWAIT timer)
; the timer will be restarted by I_EXT
; if a carrier is detected while running
; State 6 (tx frame abort, virtual state)
;
; State 7 (first EOT fill byte)
; Send dummy data to SIO, state = 5.
; State 8 (second EOT fill byte)
; Send dummy data to SIO, state = 6.
I_TBE:
LD A,(IY+TSTA) ;Get current state
OR A ;Idle state (0)?
JR Z,SPURTI ;Yes, ignore int.
DEC A ;Waiting state (1)?
JR NZ,TXS2 ;No
SPURTI: LD C,(IY+CPORT) ;SIO Control port
LD A,RESTXI ;Reset tx interrupt
OUT (C),A
RET
TXS2: DEC A ;Transmitting state (2)?
JR NZ,TXS3 ;No
CALL SIOTXCH ;Get next byte
; CALL NEXTHO ;Get next byte
OR A ;Is it a data byte?
LD D,A ;Save return code
JP M,TXBYTE ;Yes, send it
LD C,(IY+CPORT) ;SIO control port
LD A,RESTXI ;Reset the interrupt
OUT (C),A
BIT ASYNC,(IY+FLAG) ;Is this an Async port?
JP NZ,TXAS3 ;Async needs different handling
LD A,D ;Get NEXTHO return value
LD (IY+TSTA),3 ;Assume end of frame
OR A
RET Z ;NEXTHO says end of frame
LD (IY+TSTA),7 ;NEXTHO says end of transmission, send fills
RET
TXAS3: LD A,D ;Restore NEXTHO result
OR A
JP Z,STFRM ;NEXTHO Says End of Frame, more to follow
LD (IY+TSTA),6 ;NEXTHO Says End of TX, send fills
JP TXS7 ;Inc State and send fill byte
TXBYTE: LD C,(IY+DPORT) ;SIO data port
OUT (C),B ;Send data byte
RET
TXS3: DEC A ;End of Frame state (3)?
JR NZ,TXS4 ;No
JP STFRM ;Restart transmission (STFRM sets TSTA)
TXS4: DEC A ;EOT? State (4)?
JP Z,TXTERM ;End of transmission (4)
DEC A ;Waiting for DWAIT Timer? (5)
JP Z,SPURTI1 ;Yes, ignore INT
DEC A ;TX Frame Abort? (6)
JP Z,SPURTI2 ;Yes, Ignore it (Does this happen?)
CP 4 ;Sending Fill Bytes (4 of'em) States 7-10
JP P,TXTERM ;State Out of Range, abort
JR NZ,TXS7 ;Not done yet, just INC state
LD (IY+TSTA),3 ;Will INC this to State 4
TXS7: INC (IY+TSTA) ;Advance to next state
LD B,7Eh ;Data for fill
JR TXBYTE ;Send fill
SPURTI1: JP SPURTI
SPURTI2: JP SPURTI
TXTERM: LD (IY+TSTA),0 ;Show us stopped
CALL RTSOFF ;Stop transmitting
CALL TXODONE ;Report finish
; CALL HODONE ;Report finish
RET
*HEADING 'External/Status Interrupt Service'
; External/status interrupt handler. Check to see if
; rx frame aborted or DCD lost. If so, kill rx frame
; by setting rx state to 0. Then, if the tx state is 0,
; return. Otherwise, tx is active, so we will
; check the TX underrun status. If underrun and tx state
; is 2, execute tx state 8 routine (terminataion).
; If no underrun error, vector through either the CTS true
; or CTS false state table, as appropriate.
I_EXT:
LD C,(IY+CPORT) ;SIO control port
LD B,RESEXT ;Reset external status
OUT (C),B
IN A,(C) ;Get Current status register
XOR (IY+DCDTGL) ;Adjust DCD Sense
LD E,A ;Save Current Status in E
XOR (IY+RR0) ;Put Delta bits in A
LD D,A ;Save in D too
LD (IY+RR0),E ;Save real bits for next time
; E is Status; D (and A) are the Delta bits
COND CTC .eq. FALSE
BIT TIMER,(IY+FLAG) ;Are we on the port running the timer?
JR Z,I_EXT0 ;Nope... Other port...
LD HL,CLKBIT
AND (HL) ;Did the clock change state? DR200
; BIT SYNC,A ;Did the clock change state? TNC
JP Z,I_EXT0 ;No, Done
LD HL,NUMTICK ;Number of transitions to about 10ms
DEC (HL)
JP NZ,1f ;No Tick Yet!
; JP NZ,I_EXT0 ;No Tick Yet!
LD A,(TICKCNT) ;GET NUMBER OF TICKS BEFORE EXECUTING
LD (HL),A ;Reset counter
CALL TICK ;My clock Ticked...
1: LD A,(CLKBIT) ;WHAT ELSE CHANGED?
XOR 0FFH
AND D ;COPY ALL BITS, RESET CLOCK BIT
RET Z ;Nothing else changed
ENDC
I_EXT0: BIT ABORT,D ;Break/Frame Abort Changed?
JP NZ,RXABRT ;Yes, DO IT!
; LD A,B ;Restore status
; AND MABORT+MDCD ;Mask all but Abort and DCD
LD A,MDCD ;Check for Carrier
AND E ;Currently present?
CP MDCD ;Status ok?
PUSH DE ;Save Status and Delta bits
CALL NZ,RXABRT ;No, Terminate rx frame
POP DE ;Restore Status...
LD A,(IY+TSTA) ;Get tx state
OR A ;Is TX Idle?
RET Z ;Yes, we don't care about the rest
CP 5 ;Waiting for DWAIT?
JP NZ,I_EXT2 ;Nope, Maybe in State 2
BIT DCD,E ;Do we have a nasty carrier?
JR Z,I_EXT1 ;Nope, see if timer is running...
CALL STDWAIT ;damn. Start dwait AGAIN!
RET ;can't send if have dcd even if dwait=0
I_EXT1: LD A,(IY+TIMRUN) ;Is a timer running?
OR A
RET NZ ;Yes, get out
JP DWDONE ;Start TX, if we can
I_EXT2: CP 2 ;State 2 (data xfer)?
JR NZ,I_XT10 ;No
BIT EOM,E ;Is underrun status?
JP NZ,TXTERM ;Terminate xmsn
I_XT10:
; BIT CTS,E ;Is CTS on? ***DR200***
; RET Z ;No, nothing to do
LD A,(IY+TIMRUN) ;Is timer running?
OR A
RET NZ ;Yes, can't do anything
LD A,(IY+TSTA) ;Get tx state
CP 1 ;Waiting for it?
JR Z,STFRM ;Yes, start the frame
RET
*HEADING 'Internal Tx Subroutines'
; STFRM - Starts transmission of frame.
; Call NEXTHO for data byte. If none, ensure that RTS
; is off and set tx state = 0. Otherwise, reset Tx
; CRC generator, send the data byte to the SIO and set
; tx state = 2.
STFRM: LD (IY+TSTA),1 ;Waiting for CTS
LD C,(IY+CPORT) ;SIO control port
; IN A,(C) ;Get RR0 Status
; BIT CTS,A ;CTS on? ***DR200***
; RET Z ;Nope, Can't send yet...
BIT ASYNC,(IY+FLAG) ;Is this an Async port?
JP NZ,1f ;Yes, doesn't use CRC's
LD A,RESTCR ;Reset TX CRC
OUT (C),A
1: CALL SIOTXCH ;Get that byte
;1: CALL NEXTHO ;Get that byte
OR A
JP P,TXTERM ;No data waiting, why did we try and start?
LD (IY+TSTA),2 ;State = 2
LD C,(IY+DPORT) ;SIO data port
OUT (C),B ;Send the data
LD C,(IY+CPORT) ;SIO control port
LD A,RESEOM ;Reset Tx EOM status
OUT (C),A
RET
; RTSON - Turns RTS on
RTSON:
LD C,(IY+CPORT) ;SIO control port
LD A,5 ;Select WR 5
OUT (C),A
LD A,(IY+WR5REG) ;Get current WR 5 Settings
OR (IY+PTTMSK1)
AND (IY+PTTMSK2) ;Set the RTS Bit Correctly
OUT (C),A
LD (IY+WR5REG),A ;Save for later
RET
; RTSOFF - Turn RTS off
RTSOFF:
LD C,(IY+CPORT) ;SIO control port
LD A,5 ;Select WR 5
OUT (C),A
LD A,(IY+WR5REG) ;Get current setting
CPL
OR (IY+PTTMSK1)
AND (IY+PTTMSK2)
CPL
OUT (C),A
LD (IY+WR5REG),A ;Save for later
RET
; DSERVE - Waiting for DWAIT to timeout
DSERVE:
PUSH DE ;Get data structure base
POP IY
LD (IY+TIMRUN),0 ;Show timer stopped
LD C,(IY+CPORT) ;SIO control register
IN A,(C) ;Get status
XOR (IY+DCDTGL) ;Adjust Sense if needed
BIT DCD,A ;Do we have a nasty carrier?
JP NZ,XSKED ;If CCZ Then we can start sending (No DCD)
JP DWDONE ;Start Sending!
; TSERVE - Timer service
TSERVE:
PUSH DE ;Get data structure base
POP IY
LD (IY+TIMRUN),0 ;Show timer stopped
JP STFRM ;Go check transmission
*HEADING 'Rx Buffer Full Interrupt Service'
; Receive buffer full interrupt routine. Get received
; char into B and exwcute based on receive state.
I_RBF:
LD C,(IY+DPORT) ;SIO data port
IN B,(C) ;Get received data byte
LD A,(IY+RSTA) ;Get rx state
OR A ;Is rx state 0?
JR NZ,RS1 ;No
; Rx State 0, the idle state, is maintained until the first
; receive interrupt. Due to the fact that the SIO passes
; FCS as data, a one byte buffer is used to insure that FCS
; never gets passed to NEXTHI.
; Store received char in buffer, state = 1
LD A,0 ;SOM For ASYNC
LD (IY+RSTA),1 ;Next state = 1
BIT ASYNC,(IY+FLAG) ;Async port?
JP NZ,RS2A ;Just pass the byte up
LD (IY+CBUF),B ;Buffer received char
RET
RS1: DEC A ;Is rx state 1?
JR NZ,RS2
; Rx states 1 and 2 - Place the received char in the
; buffer, sending the current buffer char to NEXTHI.
; Set state = 2
LD A,0FFH ;It's a Data byte for ASYNC
BIT ASYNC,(IY+FLAG) ;Async port?
JP NZ,RS2A ;Just pass the byte up
LD (IY+RSTA),2 ;Next state = 2
LD A,0 ;Flag SOM to NEXTHI
JR GIVERX
RS2: DEC A ;Insure we are in state 2
JR NZ,RXTERM ;Error, abort frame (Async Break Recd)
LD A,0FFH ;Flag next data byte to NEXTHI
GIVERX: LD C,(IY+CBUF) ;Save buffer char
LD (IY+CBUF),B ;Buffer received char
LD B,C ;Get old buffer char
RS2A: CALL SIORXCH ;Give to upper level sub
;RS2A: CALL NEXTHI ;Give to upper level sub
OR A ;WAS THERE AN ERROR?
RET Z ;Not yet... we're ok
RXTERM: XOR A
OR (IY+RSTA) ;Only Abort once
RET Z ;Already Idle
LD (IY+RSTA),0 ;Idle state
LD A,2
CALL SIORXCH
; CALL NEXTHI
RET
global OS_QUE, _ASYBRK ;, _PADFWD
RXABRT: BIT ASYNC,(IY+FLAG) ;Async line?
JP Z,RXTERM ;No, abort current frame
BIT ABORT,D ;Abort Changed?
JP Z,RXTERM ;Lost Carrier, abort frame
BIT ABORT,E ;Is it the Start of the Break?
LD BC,1 ;Assume End of Break.
JP Z,1f ;It is...
LD (IY+RSTA),0 ;Abort frame with Break Char
LD A,1 ;Forward current rx frame when the
CALL SIORXCH ;Break sequence starts
LD BC,0 ;Indicate Start of Break
;
; Async Break Signal, call ASYBRK(chan)
;
1: LD HL,_ASYBRK ;Function to call
LD D,0
LD E,(IY+CHAN) ;Get Port number
LD A,2 ;Two Args
CALL OS_QUE
; LD A,0FFh
; LD (_PADFWD),A ;Set flag to not forward buffer when idle
RET
; Special receive interrupt routine. Occurs on error
; or end of frame. If in state 2 and no error,
; execute state 3 routine (valid EOM). If error status,
; terminate frame. Possible errors: CRC error, rx
; underrun, invalid residue code (not 8-bit boundry)
I_SRX:
LD C,(IY+CPORT) ;SIO control port
LD A,1 ;Set RR 1
OUT (C),A
IN A,(C) ;Get special rx status
LD B,A ;Save it
LD A,RESERR ;Reset error status
OUT (C),A
LD C,(IY+DPORT) ;SIO data port
IN A,(C) ;Get last FCS byte
LD A,B ;Get status
AND MEOF+MFERR+MOVR+MRES
;Save the important bits
CP MEOF+RES8 ;Proper end of frame?
JR NZ,RXTERM ;No, terminate frame
LD A,(IY+RSTA) ;We in state 2?
CP 2
JP NZ,RXTERM ;Can this frame
LD A,1 ;NEXTHI final flag
CALL SIORXCH ;Tell NEXTHI is a good one
; CALL NEXTHI ;Tell NEXTHI is a good one
LD (IY+RSTA),0 ;Reset to state 0
RET
GLOBAL SIOTXCH, SIORXCH, TXODONE ;These are now called directly
COND 0
*HEADING 'External subroutine calls'
NEXTHO:
LD L,(IY+NXTHO) ;Get NXTHO address
LD H,(IY+(NXTHO+1))
JP (HL)
NEXTHI:
LD L,(IY+NXTHI) ;Get NXTHI address
LD H,(IY+(NXTHI+1))
JP (HL)
HODONE:
LD L,(IY+ODONE) ;Get ODONE address
LD H,(IY+(ODONE+1))
JP (HL)
ENDC
GLOBAL CLKBIT, TICKCNT
PSECT bss
NUMTICK: DEFS 1
CLKBIT: DEFS 1
TICKCNT: DEFS 1
END